#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
人脸检测模块
Face Detection Module

封装YOLO11人脸检测逻辑，提供统一的检测接口
"""

import copy
import logging
import time
from typing import Any, Dict, List, Optional, Tuple

import cv2
import numpy as np

from .detection_core import YOLO11Detect
from ..config.settings import DetectionConfig, ModelConfig, Settings

logger = logging.getLogger("FaceDetection")


class YOLO11_FaceDetect:
    """
    YOLO11人脸检测类
    
    封装原有的YOLO11_Detect类，提供统一的人脸检测接口
    """
    
    def __init__(
        self,
        settings: Settings,
        model_path: Optional[str] = None,
        conf_threshold: Optional[float] = None,
        iou_threshold: Optional[float] = None,
    ):
        """
        初始化人脸检测器
        
        Args:
            settings: 全局配置对象
            model_path: 模型文件路径
            conf_threshold: 置信度阈值
            iou_threshold: IOU阈值
        """
        self.settings = settings
        self.model_config: ModelConfig = copy.deepcopy(settings.model)
        self.detection_config: DetectionConfig = copy.deepcopy(settings.detection)

        if model_path:
            self.model_config.path = model_path
        if conf_threshold is not None:
            self.model_config.confidence_threshold = conf_threshold
        if iou_threshold is not None:
            self.model_config.iou_threshold = iou_threshold

        # 检测统计
        self.detection_stats = {
            'total_detections': 0,
            'total_inference_time': 0.0,
            'average_inference_time': 0.0,
            'last_detection_time': 0.0,
            'faces_detected': 0
        }

        self.detector: Optional[YOLO11Detect] = None

        # 初始化检测器
        self._init_detector()

        logger.info("人脸检测器初始化完成: %s", self.model_config.path)
        logger.info("置信度阈值: %.2f, IOU阈值: %.2f",
                    self.model_config.confidence_threshold,
                    self.model_config.iou_threshold)

    def _init_detector(self):
        """初始化检测器"""
        try:
            self.detector = YOLO11Detect(
                model_file=self.model_config.path,
                model_config=self.model_config,
                conf=self.model_config.confidence_threshold,
                iou=self.model_config.iou_threshold,
            )
            logger.info("YOLO11检测器初始化成功")
        except Exception as e:
            logger.error("检测器初始化失败: %s", e)
            self.detector = None
            raise
    
    def detect_faces(self, frame: np.ndarray) -> Tuple[List[List[int]], float, int]:
        """
        检测人脸

        Args:
            frame: 输入图像帧

        Returns:
            Tuple[List[List[int]], float, int]: (边界框列表, 推理时间, 人脸数量)
        """
        if self.detector is None:
            logger.warning("检测器未初始化")
            return [], 0.0, 0

        start_time = time.time()

        try:
            input_tensor = self.detector.bgr2nv12(frame)

            outputs = self.detector.forward(input_tensor)

            numpy_outputs = self.detector.c2numpy(outputs)

            class_ids, confidences, bboxes = self.detector.post_process(numpy_outputs)

            # 计算推理时间
            inference_time = time.time() - start_time

            # 转换边界框格式为整数列表
            face_boxes: List[List[int]] = []
            if bboxes is not None and len(bboxes) > 0:
                for bbox in bboxes:
                    # 确保坐标为整数
                    x1, y1, x2, y2 = map(int, bbox)
                    face_boxes.append([x1, y1, x2, y2])

            face_count = len(face_boxes)

            # 更新统计信息
            self._update_stats(inference_time, face_count)

            logger.debug(f"检测到 {face_count} 个人脸，推理时间: {inference_time:.3f}s")

            return face_boxes, inference_time, face_count

        except Exception as e:
            logger.error("人脸检测失败: %s", e)
            return [], 0.0, 0
    
    def _update_stats(self, inference_time: float, face_count: int):
        """更新检测统计信息"""
        self.detection_stats['total_detections'] += 1
        self.detection_stats['total_inference_time'] += inference_time
        self.detection_stats['average_inference_time'] = (
            self.detection_stats['total_inference_time'] / 
            self.detection_stats['total_detections']
        )
        self.detection_stats['last_detection_time'] = inference_time
        self.detection_stats['faces_detected'] = face_count
    
    def get_stats(self) -> Dict[str, Any]:
        """获取检测统计信息"""
        return self.detection_stats.copy()
    
    def draw_detections(self, frame: np.ndarray, face_boxes: List[List[int]],
                       draw_center: bool = True, tracking_enabled: bool = False) -> np.ndarray:
        """
        在图像上绘制检测结果

        Args:
            frame: 输入图像
            face_boxes: 人脸边界框列表
            draw_center: 是否绘制中心点
            tracking_enabled: 是否开启追踪模式（决定检测框颜色）

        Returns:
            np.ndarray: 绘制后的图像
        """
        result_frame = frame.copy()

        if not face_boxes:
            return result_frame

        # 根据追踪状态选择颜色
        # 追踪开启时使用橙色 (0, 165, 255)，未开启时使用绿色 (0, 255, 0)
        box_color = (0, 165, 255) if tracking_enabled else (0, 255, 0)

        # 绘制人脸框
        for i, bbox in enumerate(face_boxes):
            x1, y1, x2, y2 = bbox

            # 绘制边界框（动态颜色）
            cv2.rectangle(result_frame, (x1, y1), (x2, y2), box_color, 2)

            # 绘制标签
            label = f"Face {i+1}"
            if tracking_enabled:
                label += " (Tracking)"
            label_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)[0]
            cv2.rectangle(result_frame, (x1, y1-label_size[1]-10),
                         (x1+label_size[0], y1), box_color, -1)
            cv2.putText(result_frame, label, (x1, y1-5),
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 0), 2)
            
            # 绘制中心点
            if draw_center:
                center_x = (x1 + x2) // 2
                center_y = (y1 + y2) // 2
                cv2.circle(result_frame, (center_x, center_y), 5, (0, 0, 255), -1)
        
        # 绘制画面中心点
        h, w = result_frame.shape[:2]
        cv2.circle(result_frame, (w//2, h//2), 8, (255, 0, 0), 2)
        cv2.line(result_frame, (w//2-15, h//2), (w//2+15, h//2), (255, 0, 0), 2)
        cv2.line(result_frame, (w//2, h//2-15), (w//2, h//2+15), (255, 0, 0), 2)
        
        # 添加统计信息
        stats_text = f"Faces: {len(face_boxes)}, Time: {self.detection_stats['last_detection_time']:.3f}s"
        cv2.putText(result_frame, stats_text, (10, 30), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
        
        return result_frame
    
    def get_largest_face(self, face_boxes: List[List[int]]) -> Optional[List[int]]:
        """
        获取最大的人脸框
        
        Args:
            face_boxes: 人脸边界框列表
            
        Returns:
            Optional[List[int]]: 最大人脸框，如果没有则返回None
        """
        if not face_boxes:
            return None
        
        largest_area = 0
        largest_face = None
        
        for bbox in face_boxes:
            x1, y1, x2, y2 = bbox
            area = (x2 - x1) * (y2 - y1)
            if area > largest_area:
                largest_area = area
                largest_face = bbox
        
        return largest_face
    
    def get_face_center(self, bbox: List[int]) -> Tuple[int, int]:
        """
        获取人脸框的中心点
        
        Args:
            bbox: 人脸边界框 [x1, y1, x2, y2]
            
        Returns:
            Tuple[int, int]: 中心点坐标 (x, y)
        """
        x1, y1, x2, y2 = bbox
        center_x = (x1 + x2) // 2
        center_y = (y1 + y2) // 2
        return center_x, center_y
    
    def calculate_offset_from_center(self, bbox: List[int], frame_shape: Tuple[int, int]) -> Tuple[float, float]:
        """
        计算人脸中心点相对于画面中心的偏移
        
        Args:
            bbox: 人脸边界框
            frame_shape: 画面尺寸 (height, width)
            
        Returns:
            Tuple[float, float]: 偏移量 (offset_x, offset_y)
        """
        h, w = frame_shape
        frame_center_x, frame_center_y = w // 2, h // 2
        
        face_center_x, face_center_y = self.get_face_center(bbox)
        
        offset_x = face_center_x - frame_center_x
        offset_y = face_center_y - frame_center_y
        
        return offset_x, offset_y
